package metamodel.misc;

import att.grappa.*;

import javax.swing.*;

import java.io.*;
import java.util.*;
import java.util.List;
import java.awt.*;

import antlr.TokenStreamException;
import antlr.RecognitionException;
import metamodel.visitor.ObjectGraphIterator;
import metamodel.visitor.DefaultGraphVisitor;
import metamodel.*;

/**
 * User: felix
 * Date: 19.06.2005
 */
public class GraphViz extends DefaultGraphVisitor {
    private Graph graph;
    private HashMap object2node=new HashMap(10);
    private boolean enforceRoot;
    private static String dotCommand = System.getProperty("os.name").startsWith("Windows")?"cmd /c dot":"dot";

    public GraphViz(ClassNode model,Object dataRoot,boolean enforceRoot) throws TokenStreamException, RecognitionException {
        this.enforceRoot=enforceRoot;
        graph=new Graph("GraphName");
   //     graph.setAttribute("rankdir","LR");
        ObjectGraphIterator it=new ObjectGraphIterator(model,this, ObjectGraphIterator.TraversalMode.NOTIFY_CYCLES);
        if (dataRoot instanceof List){
            it.iterateCollection((List) dataRoot);
        }else{
            it.iterate(dataRoot);
        }


    }

    public Graph getGraph() {
        return graph;
    }



    String join(java.util.List l,String delimiter){
        StringBuffer ret=new StringBuffer();
        for (Iterator iterator = l.iterator(); iterator.hasNext();) {
            String s = (String) iterator.next();
            ret.append(s);
            if (iterator.hasNext()){
                ret.append(delimiter);
            }
        }
        return ret.toString();
    }

    public void visitRoot(ClassNode classNode, Object data) {
        Subgraph graph;
        if (enforceRoot){
            graph = new Subgraph(this.graph);
        }else
        {graph=this.graph;};
        createObjectNode(graph, classNode, data);
    }

    public void visitObject(ClassNode classNode, DeepProperty property, Object value, Object traversalParent) {
        Node parentNode =(Node) object2node.get(traversalParent);
        if (object2node.containsKey(value)){
            throw new RuntimeException("Visited node twice");
        }
        Node me = createObjectNode(graph, classNode, value);


        if (property instanceof CollectionRef){
             new Edge(graph, parentNode,me);
            }   else{
               new Edge(graph, parentNode,me).setAttribute("label",property.getName());
            }

     }

    private Node createObjectNode(Subgraph graph, ClassNode classNode, Object value) {
        Node me;
        me =new Node(graph);
        me.setAttribute("shape","record");
        ArrayList properties=new ArrayList();
        for (Iterator iterator = classNode.getProperties().iterator(); iterator.hasNext();) {
            Property p = (Property) iterator.next();
            /*if (p instanceof DeepProperty) {
                DeepProperty deepProperty = (DeepProperty) p;
                properties.add("<"+p.getName()+">"+p.getName());
            }*/
            if (p instanceof FlatProperty) {
                properties.add("<"+p.getName()+">"+p.getName()+"=\""+p.getValue(value)+"\"");
            }
        }
        me.setAttribute("label","{<classhead>"+value.toString()+"|"+join(properties,"|")+"}");
        object2node.put(value,me);
        return me;
    }

    public void visitCollection(CollectionRef property, Object value, Object parent) {
        Node parentNode = (Node) object2node.get(parent);
        Node collectionNode=new Node(graph);
        object2node.put(value,collectionNode);
        collectionNode.setAttribute("label","");
        collectionNode.setAttribute("shape","point");
        Edge edge = new Edge(graph, parentNode, collectionNode);
        edge.setAttribute("label",property.getName());
        edge.setAttribute("arrowhead","none");
    }

    public void visitFlatProperty(FlatProperty property, Object value, Object parent, boolean hasNext) {}

    public void visitCyclic(ClassNode classNode, DeepProperty property, Object value, Object traversalParent, boolean hasNext) {
        Node me =(Node) object2node.get(value);
        Node parentNode=(Node) object2node.get(traversalParent);
        Edge e = new Edge(graph, parentNode, me);
        if (property instanceof ObjectReference){
            e.setAttribute("label",property.getName());
        }

    }


    public static void main(String[] args) throws IOException, TokenStreamException, RecognitionException {
        ClassNode metamodel= TraversalUtils.createMetaModel();
        graph(metamodel, metamodel,true, "Graph Test");
    }

    public static void graph(ClassNode model, Object dataRoot) throws IOException, TokenStreamException, RecognitionException
    {
        graph(model,dataRoot,true, "Graph Test");
    }

    public static void graph(ClassNode model, Object dataRoot, String title) throws IOException, TokenStreamException, RecognitionException
    {
        graph(model,dataRoot,true, title);
    }

    public static void writeDot(ClassNode model, Object dataRoot, boolean enforce, String fileName){
        try {
            GraphViz test=new GraphViz(model,dataRoot,enforce);

            Writer theGraph = new FileWriter(fileName);

    		test.getGraph().printGraph(theGraph);
    		theGraph.close();


        } catch (TokenStreamException e) {
            throw new RuntimeException(e);
        } catch (RecognitionException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }

    public static void graph(ClassNode model, Object dataRoot, boolean enforce, String title) throws TokenStreamException, RecognitionException, IOException
    {
        JFrame frame = new JFrame(title);
        GraphViz test=new GraphViz(model,dataRoot,enforce);
        GrappaPanel comp = new GrappaPanel(test.getGraph());
        comp.setPreferredSize(new Dimension(600,400));
        StringWriter theGraph = new StringWriter();
    		test.getGraph().printGraph(theGraph);
    		theGraph.flush();
        System.out.println(theGraph.getBuffer());

        Object connector = Runtime.getRuntime().exec(dotCommand);

        GrappaSupport.filterGraph(test.getGraph(),connector);
        frame.getContentPane().add(new JScrollPane(comp));
        frame.pack();
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frame.show();
    }

    public static void setDotCommand(String dotCommand) {
        GraphViz.dotCommand = dotCommand;
    }
}
